﻿using ArcSoftFace.SDKModels;
using ArcSoftFace.SDKUtil;
using ArcSoftFace.Utils;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;
using ArcSoftFace.Entity;
using System.Drawing.Imaging;
using ArcSoftFaceTool.Utils;

namespace TestArcSoftFace
{
    public class TestFace
    {
        #region 参数定义
        /// <summary>
        /// 引擎Handle
        /// </summary>
        private IntPtr pImageEngine = IntPtr.Zero;


        #region 视频模式下相关
        /// <summary>
        /// 视频引擎Handle
        /// </summary>
        private IntPtr pVideoEngine = IntPtr.Zero;
        /// <summary>
        /// RGB视频引擎 FR Handle 处理   FR和图片引擎分开，减少强占引擎的问题
        /// </summary>
        private IntPtr pVideoRGBImageEngine = IntPtr.Zero;
        /// <summary>
        /// IR视频引擎 FR Handle 处理   FR和图片引擎分开，减少强占引擎的问题
        /// </summary>
        private IntPtr pVideoIRImageEngine = IntPtr.Zero;
        #endregion

        #endregion

        /// <summary>
        /// 初始化引擎
        /// </summary>
        private void InitEngines()
        {
            //读取配置文件
            string appId = "59BCFKXVCtRkRYa8d4QjWkgVAF9AieGhq1XhWztnNuJk";
            string sdkKey64 = "CQkBiCairzYJbMJyMnP96h1LmtrGX4W6mb6Ps32gUq3c";
            string sdkKey32 = "CQkBiCairzYJbMJyMnP96h1Lied2JW9F5vyVXJFsKfzF";


            //在线激活引擎    如出现错误，1.请先确认从官网下载的sdk库已放到对应的bin中，2.当前选择的CPU为x86或者x64
            int retCode = 0;
            try
            {
                string sdkKey = sdkKey32;
                retCode = ASFFunctions.ASFOnlineActivation(appId, sdkKey);
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("无法加载 DLL"))
                {
                    Console.WriteLine("请将sdk相关DLL放入bin对应的x86或x64下的文件夹中!");
                }
                else
                {
                    Console.WriteLine("激活引擎失败!");
                }
                return;
            }
            Console.WriteLine("Activate Result:" + retCode);

            //初始化引擎
            uint detectMode = DetectionMode.ASF_DETECT_MODE_IMAGE;
            //Video模式下检测脸部的角度优先值
            int videoDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_HIGHER_EXT;
            //Image模式下检测脸部的角度优先值
            int imageDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_ONLY;
            //人脸在图片中所占比例，如果需要调整检测人脸尺寸请修改此值，有效数值为2-32
            int detectFaceScaleVal = 16;
            //最大需要检测的人脸个数
            int detectFaceMaxNum = 5;
            //引擎初始化时需要初始化的检测功能组合
            int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_AGE | FaceEngineMask.ASF_GENDER | FaceEngineMask.ASF_FACE3DANGLE;
            //初始化引擎，正常值为0，其他返回值请参考http://ai.arcsoft.com.cn/bbs/forum.php?mod=viewthread&tid=19&_dsign=dbad527e
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal,
                detectFaceMaxNum, combinedMask, ref pImageEngine);
            Console.WriteLine("pImageEngine InitEngine Result:" + retCode);
            Console.WriteLine((retCode == 0) ? "引擎初始化成功!\n" : string.Format("引擎初始化失败!错误码为:{0}\n", retCode));


            //初始化视频模式下人脸检测引擎
            uint detectModeVideo = DetectionMode.ASF_DETECT_MODE_VIDEO;
            int combinedMaskVideo = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION;
            retCode = ASFFunctions.ASFInitEngine(detectModeVideo, videoDetectFaceOrientPriority, detectFaceScaleVal,
                detectFaceMaxNum, combinedMaskVideo, ref pVideoEngine);
            Console.WriteLine("combinedMaskVideo InitVideoEngine Result:" + retCode);

            //RGB视频专用FR引擎
            detectFaceMaxNum = 1;
            combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_LIVENESS;
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal,
                detectFaceMaxNum, combinedMask, ref pVideoRGBImageEngine);
            Console.WriteLine("pVideoRGBImageEngine InitVideoEngine Result:" + retCode);

            //IR视频专用FR引擎
            combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_IR_LIVENESS;
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal,
                detectFaceMaxNum, combinedMask, ref pVideoIRImageEngine);

            Console.WriteLine("pVideoIRImageEngine InitVideoEngine Result:" + retCode);

        }

        private void UninitEngine()
        {
            //销毁引擎
            int retCode = ASFFunctions.ASFUninitEngine(pImageEngine);
            Console.WriteLine("UninitEngine pImageEngine Result:" + retCode);
            //销毁引擎
            retCode = ASFFunctions.ASFUninitEngine(pVideoEngine);
            Console.WriteLine("UninitEngine pVideoEngine Result:" + retCode);

            //销毁引擎
            retCode = ASFFunctions.ASFUninitEngine(pVideoRGBImageEngine);
            Console.WriteLine("UninitEngine pVideoImageEngine Result:" + retCode);

            //销毁引擎
            retCode = ASFFunctions.ASFUninitEngine(pVideoIRImageEngine);
            Console.WriteLine("UninitEngine pVideoIRImageEngine Result:" + retCode);
        }


        public void Run()
        {
            //InitEngines();

            ////TestDetectFace();
            ////TestDetectFace1();
            //TestDetectFaceLoop();

            //UninitEngine();

            string sPath = Path.Combine(Directory.GetCurrentDirectory(), "image");
            if (!Directory.Exists(sPath))
            {
                Console.WriteLine(sPath + " 地址不存在！");
                return;
            }
            string[] sFiles = Directory.GetFiles(sPath, "*.jpg");

            foreach (var item in sFiles)
            {
                FaceDetectionUtilTest(item);
            }
        }

        /// <summary>
        /// 测试人脸检测
        /// </summary>
        private void TestDetectFace()
        {

            string sBasePath = Path.Combine(Directory.GetCurrentDirectory(), "image");
            Stopwatch stopwatch = new Stopwatch();

            //注册人脸，检测，并提取人脸特征码
            string sFile = Path.Combine(sBasePath, "9.jpg");
            Image image = ImageUtil.readFromFile(sFile);
            stopwatch.Restart();
            ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pImageEngine, image);
            Console.WriteLine($"{sFile} 人脸数：{multiFaceInfo.faceNum} 人脸检测耗时：{stopwatch.ElapsedMilliseconds}");
            stopwatch.Stop();
            if (multiFaceInfo.faceNum > 0)
            {
                //ASF_SingleFaceInfo singleFaceInfo = new ASF_SingleFaceInfo();
                //stopwatch.Restart();
                ////提取人脸特征
                //IntPtr feature = FaceUtil.ExtractFeature(pImageEngine, image, out singleFaceInfo);
                //stopwatch.Stop();
                //Console.WriteLine($"{sFile} 人脸特征码提取完毕 耗时：{stopwatch.ElapsedMilliseconds}");



                //进行人脸对比
                var sFiles = Directory.GetFiles(sBasePath);
                foreach (var sItemFile in sFiles)
                {
                    Console.WriteLine($"------------{sItemFile} 开始比对 ------------ ");
                    //读取图像
                    Image srcImage = ImageUtil.readFromFile(sItemFile);
                    //调整图片数据，非常重要
                    ImageInfo imageInfo = ImageUtil.ReadBMP(srcImage);
                    if (imageInfo == null)
                    {
                        Console.WriteLine("图像数据获取失败，请稍后重试!");
                        Console.WriteLine($"----------检测结束---------");
                        continue;
                    }
                    //人脸检测
                    stopwatch.Restart();
                    ASF_MultiFaceInfo cMultiFaceInfo = FaceUtil.DetectFace(pImageEngine, imageInfo);
                    stopwatch.Stop();
                    if (cMultiFaceInfo.faceNum < 1)
                    {

                        Console.WriteLine($"未检测出人脸! 耗时 {stopwatch.ElapsedMilliseconds} ms");
                        Console.WriteLine($"----------检测结束---------");
                        continue;
                    }
                    else
                    {
                        Console.WriteLine($"检测到人脸数量：{cMultiFaceInfo.faceNum } 耗时 {stopwatch.ElapsedMilliseconds} ms");
                    }

                    /*
                    //年龄检测
                    stopwatch.Restart();
                    int retCode_Age = -1;
                    ASF_AgeInfo ageInfo = FaceUtil.AgeEstimation(pImageEngine, imageInfo, cMultiFaceInfo, out retCode_Age);
                    stopwatch.Stop();

                    Console.WriteLine($"年龄检测：{retCode_Age } 耗时 {stopwatch.ElapsedMilliseconds} ms");

                    //性别检测
                    stopwatch.Restart();
                    int retCode_Gender = -1;
                    ASF_GenderInfo genderInfo = FaceUtil.GenderEstimation(pImageEngine, imageInfo, cMultiFaceInfo, out retCode_Gender);
                    stopwatch.Stop();
                    Console.WriteLine($"性别检测：{retCode_Gender } 耗时 {stopwatch.ElapsedMilliseconds} ms");

                    //3DAngle检测
                    stopwatch.Restart();
                    int retCode_3DAngle = -1;
                    ASF_Face3DAngle face3DAngleInfo = FaceUtil.Face3DAngleDetection(pImageEngine, imageInfo, cMultiFaceInfo, out retCode_3DAngle);
                    stopwatch.Stop();
                    Console.WriteLine($"3DAngle：{retCode_3DAngle } 耗时 {stopwatch.ElapsedMilliseconds} ms");

                    //打印检测到的信息
                    for (int i = 0; i < cMultiFaceInfo.faceNum; i++)
                    {
                        MRECT rect = MemoryUtil.PtrToStructure<MRECT>(cMultiFaceInfo.faceRects + MemoryUtil.SizeOf<MRECT>() * i);
                        int orient = MemoryUtil.PtrToStructure<int>(cMultiFaceInfo.faceOrients + MemoryUtil.SizeOf<int>() * i);
                        int age = 0;

                        if (retCode_Age != 0)
                        {
                            Console.WriteLine("年龄检测失败!");
                        }
                        else
                        {
                            age = MemoryUtil.PtrToStructure<int>(ageInfo.ageArray + MemoryUtil.SizeOf<int>() * i);
                        }

                        int gender = -1;
                        if (retCode_Gender != 0)
                        {
                            Console.WriteLine("性别检测失败!");
                        }
                        else
                        {
                            gender = MemoryUtil.PtrToStructure<int>(genderInfo.genderArray + MemoryUtil.SizeOf<int>() * i);
                        }


                        int face3DStatus = -1;
                        float roll = 0f;
                        float pitch = 0f;
                        float yaw = 0f;
                        if (retCode_3DAngle != 0)
                        {
                            Console.WriteLine("3DAngle检测失败!");
                        }
                        else
                        {
                            //角度状态 非0表示人脸不可信
                            face3DStatus = MemoryUtil.PtrToStructure<int>(face3DAngleInfo.status + MemoryUtil.SizeOf<int>() * i);
                            //roll为侧倾角，pitch为俯仰角，yaw为偏航角
                            roll = MemoryUtil.PtrToStructure<float>(face3DAngleInfo.roll + MemoryUtil.SizeOf<float>() * i);
                            pitch = MemoryUtil.PtrToStructure<float>(face3DAngleInfo.pitch + MemoryUtil.SizeOf<float>() * i);
                            yaw = MemoryUtil.PtrToStructure<float>(face3DAngleInfo.yaw + MemoryUtil.SizeOf<float>() * i);
                        }
                        int rectWidth = rect.right - rect.left;
                        int rectHeight = rect.bottom - rect.top;

                        StringBuilder sBuf = new StringBuilder();
                        sBuf.Append($"人脸坐标:[left:{rect.left},top:{rect.top},right:{rect.right},bottom:{rect.bottom}]").AppendLine();
                        sBuf.Append($"角度：{orient}，侧倾角：{roll}，俯仰角：{pitch}，偏航角：{yaw},角度状态：{face3DStatus}( 非0表示人脸不可信)").AppendLine();

                        sBuf.Append($"年龄:{age} 岁，性别：");
                        if (gender == 0)
                        {
                            sBuf.Append("男");
                        }
                        else if (gender == 1)
                        {
                            sBuf.Append("女");
                        }

                        Console.WriteLine(sBuf.ToString());
                    }

                    ASF_SingleFaceInfo cSingleFaceInfo = new ASF_SingleFaceInfo();
                    //提取人脸特征
                    stopwatch.Restart();
                    IntPtr cFeature = FaceUtil.ExtractFeature(pImageEngine, srcImage, out cSingleFaceInfo);
                    stopwatch.Stop();
                    Console.WriteLine($"人脸特征码提取完毕 耗时 {stopwatch.ElapsedMilliseconds} ms");

                    //开始比对
                    float similarity = 0f;
                    stopwatch.Restart();
                    int ret = ASFFunctions.ASFFaceFeatureCompare(pImageEngine, cFeature, feature, ref similarity);
                    stopwatch.Stop();
                    Console.WriteLine($"人脸比对完毕 耗时 {stopwatch.ElapsedMilliseconds} ms");
                    //增加异常值处理
                    if (similarity.ToString().IndexOf("E") > -1)
                    {
                        similarity = 0f;
                    }
                    Console.WriteLine($"人脸相似度：{similarity}");


                    MemoryUtil.Free(cFeature);*/
                    MemoryUtil.Free(imageInfo.imgData);
                    Console.WriteLine("----------检测结束---------");
                    Console.WriteLine();
                }



                //MemoryUtil.Free(feature);

            }
        }


        private void TestDetectFace1()
        {
            string sBasePath = Path.Combine(Directory.GetCurrentDirectory(), "image");
            Stopwatch stopwatch = new Stopwatch();

            //注册人脸，检测，并提取人脸特征码
            string sFile = Path.Combine(sBasePath, "3_mini.jpg");
            byte[] arr = File.ReadAllBytes(sFile);

            MemoryStream msSave = new MemoryStream(arr);

            Bitmap bmp = new Bitmap(msSave);
            msSave.Close();
            msSave.Dispose();
            msSave = null;
            arr = null;

            Bitmap bmpFace = bmp;
            if (bmp.Width > 480 || bmp.Height > 640 || bmp.Width % 4 != 0)
            {
                bmpFace = (Bitmap)ImageUtil.ScaleImage(bmp, 480, 640);
                bmp.Dispose();
            }
            bmp = null;

            stopwatch.Restart();

            ASF_MultiFaceInfo multiFaceInfo = new ASF_MultiFaceInfo();

            BitmapData data = bmpFace.LockBits(new Rectangle(0, 0, bmpFace.Width, bmpFace.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            try
            {
                //位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行
                IntPtr ptr = data.Scan0;

                ImageInfo imageInfo = new ImageInfo();

                //填充引用对象字段值
                imageInfo.width = data.Width;
                imageInfo.height = data.Height;
                imageInfo.format = (int)ASF_ImagePixelFormat.ASVL_PAF_RGB24_B8G8R8;

                imageInfo.imgData = ptr;
                multiFaceInfo = FaceUtil.DetectFace(pImageEngine, imageInfo);
                imageInfo = null;
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                bmpFace.UnlockBits(data);
                bmpFace.Dispose();
                bmpFace = null;
            }
            data = null;
            stopwatch.Stop();


            Console.WriteLine($"{sFile} 人脸数：{multiFaceInfo.faceNum} 人脸检测耗时：{stopwatch.ElapsedMilliseconds}");

        }

        /// <summary>
        /// 循环测试
        /// </summary>
        private void TestDetectFaceLoop()
        {
            string sBasePath = Path.Combine(Directory.GetCurrentDirectory(), "image");
            Stopwatch stopwatch = new Stopwatch();

            //注册人脸，检测，并提取人脸特征码
            string sFile = Path.Combine(sBasePath, "3_mini.jpg");

            byte[] arr = File.ReadAllBytes(sFile);
            stopwatch.Restart();
            MemoryStream msSave = new MemoryStream(arr);

            Bitmap bmp = new Bitmap(msSave);
            msSave.Close();
            msSave.Dispose();
            msSave = null;

            arr = null;

            Bitmap bmpFace = bmp;
            if (bmp.Width > 480 || bmp.Height > 640 || bmp.Width % 4 != 0)
            {
                bmpFace = (Bitmap)ImageUtil.ScaleImage(bmp, 480, 640);
                bmp.Dispose();
            }
            bmp = null;
            stopwatch.Stop();
            Console.WriteLine($"图片加载及压缩裁剪处理耗时：{stopwatch.ElapsedMilliseconds}");


            int iCount = 1;
            do
            {
                stopwatch.Restart();
                var faceNum = LoopTestStep(bmpFace);
                stopwatch.Stop();

                Console.WriteLine($" {iCount} -- {sFile} 人脸数：{faceNum} 人脸检测耗时：{stopwatch.ElapsedMilliseconds}");


                iCount++;
            } while (iCount < 100000);


            bmpFace.Dispose();
            bmpFace = null;


        }

        private int LoopTestStep(Bitmap bmpFace)
        {

            ASF_MultiFaceInfo multiFaceInfo = new ASF_MultiFaceInfo();

            BitmapData data = bmpFace.LockBits(new Rectangle(0, 0, bmpFace.Width, bmpFace.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            try
            {
                //位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行
                IntPtr ptr = data.Scan0;

                ImageInfo imageInfo = new ImageInfo();

                //填充引用对象字段值
                imageInfo.width = data.Width;
                imageInfo.height = data.Height;
                imageInfo.format = (int)ASF_ImagePixelFormat.ASVL_PAF_RGB24_B8G8R8;

                imageInfo.imgData = ptr;
                multiFaceInfo = FaceUtil.DetectFace(pImageEngine, imageInfo);
                imageInfo = null;
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                bmpFace.UnlockBits(data);
                //bmpFace.Dispose();
                //bmpFace = null;
                data = null;
            }


            return multiFaceInfo.faceNum;

        }

        private void FaceDetectionUtilTest(string sName)
        {
            string appId = "59BCFKXVCtRkRYa8d4QjWkgVAF9AieGhq1XhWztnNuJk";
            string sdkKey64 = "CQkBiCairzYJbMJyMnP96h1LmtrGX4W6mb6Ps32gUq3c";
            string sdkKey32 = "CQkBiCairzYJbMJyMnP96h1Lied2JW9F5vyVXJFsKfzF";
            bool is64CPU = Environment.Is64BitProcess;


            FaceDetectionUtil.AppId = appId;
            if (is64CPU)
                FaceDetectionUtil.SDKKey = sdkKey64;
            else
                FaceDetectionUtil.SDKKey = sdkKey32;

            FaceDetectionUtil dettool = FaceDetectionUtil.GetInstance();

            string sBasePath = Path.Combine(Directory.GetCurrentDirectory(), "image");
            Stopwatch stopwatch = new Stopwatch();
            string sFile = Path.Combine(sBasePath, sName);
            using (Bitmap image = (Bitmap)ImageUtil.readFromFile(sFile))
            {
                stopwatch.Restart();
                dettool.FaceDetection(image, out ASF_MultiFaceInfo multiFaceInfo);
                stopwatch.Stop();
                Console.WriteLine($"{Path.GetFileName(sFile)} 人脸数：{multiFaceInfo.faceNum} 人脸检测耗时：{stopwatch.ElapsedMilliseconds}");
            }


        }

    }


}
